package com.lyncs.ods.task;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import com.lyncs.ods.cache.CurrencyRateCache;
import com.lyncs.ods.constant.CurrencyEnum;
import com.lyncs.ods.constant.LyncsOdsConstant;
import com.lyncs.ods.modules.setting.model.CurrencyRateSetting;
import com.lyncs.ods.modules.setting.service.CurrencyRateSettingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author alex
 * @date 2022-04-03 10:37
 */
@Component
@Slf4j
public class CurrencyTask implements ApplicationRunner {

    @Resource
    private CurrencyRateSettingService currencyRateSettingService;
    /**
     * IT 工厂自定义域名
     */
    @Value("${currency.domain}")
    private String domain;
    /**
     * IT 工厂获取汇率的URL（QPS:10）
     */
    @Value("${currency.url}")
    private String url;

    @Scheduled(cron = "0 0 0 ? * *", zone = "GMT+8:00")
    public void run() {
        List<CurrencyRateSetting> rateSettings = getDbCurrencyRate();
        if (CollectionUtil.isEmpty(rateSettings)) {
            rateSettings = getIt120CurrencyRate();
        }
        if (CollectionUtil.isEmpty(rateSettings)) {
            log.error("cannot get currency rate,date:{}", LocalDate.now());
            return;
        }
        log.info("refresh currency setting,date:{},data:{}", LocalDate.now(), JSONObject.toJSONString(rateSettings));

        rateSettings.forEach(rateSetting -> CurrencyRateCache.put(rateSetting.getFromCurrency(), rateSetting.getToCurrency(), rateSetting.getRate(), Boolean.TRUE));
    }

    /**
     * 已入库数据直接加载
     *
     * @return 汇率信息
     */
    List<CurrencyRateSetting> getDbCurrencyRate() {
        return currencyRateSettingService.lambdaQuery().eq(CurrencyRateSetting::getDate, LocalDate.now()).eq(CurrencyRateSetting::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey()).list();
    }

    /**
     * 未入库数据从it工厂获取并入库
     * QPS limit: 10
     *
     * @return 汇率信息
     */
    List<CurrencyRateSetting> getIt120CurrencyRate() {
        LocalDate today = LocalDate.now();
        List<CurrencyRateSetting> rateSettings = new ArrayList<>();
        //所有汇率的基础转换货币为CNY
        List<String> currencyCodes = Stream.of(CurrencyEnum.values())
                .filter(e -> LyncsOdsConstant.EnableStatus.ENABLE.getKey().equals(e.getStatus()))
                .filter(e -> !e.equals(CurrencyEnum.CNY))
                .map(CurrencyEnum::getCurrency)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(currencyCodes)) {
            return rateSettings;
        }
        //https://xbronc.com/freeapi/exchangeRateVisa?base=USD&obj=CNY
        String domainUrl = StrUtil.format(url, domain);
        currencyCodes.forEach(code -> {
            String toCode = CurrencyEnum.CNY.getCurrency();
            Map<String, Object> paramMap = Map.of("fromCode", code, "toCode", toCode);
            String ret = HttpUtil.get(domainUrl, paramMap);
            if (StringUtils.isEmpty(ret)) {
                log.error("cannot get currency rate info from:{},date:{},fromCode:{},toCode:{}", domainUrl, today, code, toCode);
                return;
            }
            if (!JSONValidator.from(ret).validate()) {
                log.error("cannot get currency rate info from:{},date:{},fromCode:{},toCode:{},result:{}", domainUrl, today, code, toCode, ret);
                return;
            }
            JSONObject jsonObject = JSONObject.parseObject(ret);
            JSONObject data = jsonObject.getJSONObject("data");
            if (Objects.isNull(data) || !data.containsKey("rate")) {
                log.error("cannot get currency rate info from:{},date:{},fromCode:{},toCode:{},result:{}", domainUrl, today, code, toCode, ret);
                return;
            }
            BigDecimal rate = data.getBigDecimal("rate");
            rateSettings.add(new CurrencyRateSetting().setFromCurrency(code).setToCurrency(toCode).setRate(rate).setDate(LocalDate.now()).setStatus(LyncsOdsConstant.EnableStatus.ENABLE.getKey()));
        });
        log.info("cache currency rate from it-120,url:{},date:{}", domainUrl, today);
        currencyRateSettingService.saveBatch(rateSettings);
        return rateSettings;
    }

    @Override
    public void run(ApplicationArguments args) {
        run();
    }
}
